C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / stickers / StickerPacksManager.vue
Displaying Raw • Download
meshchatx/src/frontend/components/stickers/StickerPacksManager.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 12.36 KB
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl bg-blue-600 hover:bg-blue-700 text-white px-3 py-1.5 text-sm font-medium flex items-center gap-1Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffopenCreatePackTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fffolder-plus-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("sticker_packs.create") }}
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-300 dark:border-zinc-600 px-3 py-1.5 text-sm hover:border-teal-500 flex items-center gap-1Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6fftriggerInstallInputTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpackage-downTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("sticker_packs.install_from_file") }}
Tff7b72</button>
Tff7b72<input
Te6edf3ref=Ta5d6ff"Ta5d6ffinstallFileInputTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff"
Te6edf3accept=Ta5d6ff"Ta5d6ff.json,application/jsonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff"
@Te6edf3change=Ta5d6ff"Ta5d6ffonInstallFileTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffpacks.length === 0Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-500 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("sticker_packs.empty") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-2 gap-3Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffpack in packsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffpack.idTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-700 p-3 bg-white/60 dark:bg-zinc-800/60 flex flex-col gap-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-2Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-semibold text-gray-800 dark:text-zinc-100 truncateTa5d6ff"Tff7b72>
{{ pack.title }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{
Te6edf3$t("sticker_packs.count_label", {
count: pack.sticker_count,
})
}}
Te6edf3·
{{ Te6edf3$t(`sticker_packs.type_Tff7b72${Te6edf3packTff7b72.Td2a8ffpack_typeTff7b72}`) }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg p-1.5 hover:bg-gray-100 dark:hover:bg-zinc-700 text-gray-600 dark:text-zinc-300Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffsticker_packs.exportTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffexportPack(pack)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffexportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg p-1.5 hover:bg-red-50 dark:hover:bg-red-950/30 text-red-600Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffsticker_packs.deleteTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdeletePack(pack)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fftrash-can-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffpack.stickers && pack.stickers.length > 0Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-6 gap-1.5 mt-1Ta5d6ff"Tff7b72>
Tff7b72<StickerView
Te6edf3v-for=Ta5d6ff"Ta5d6ffs in pack.stickers.slice(0, 12)Ta5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffs.idTa5d6ff"
Te6edf3:src=Ta5d6ff"Ta5d6ffstickerImageUrl(s.id)Ta5d6ff"
Te6edf3:image-type=Ta5d6ff"Ta5d6ffs.image_typeTa5d6ff"
Te6edf3size=Ta5d6ff"Ta5d6ffxsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded border border-gray-200 dark:border-zinc-700 bg-gray-50 dark:bg-zinc-900Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400 italic mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("sticker_packs.empty_pack") }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffcreateOpenTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffixed inset-0 z-[150] flex items-center justify-center bg-black/60 p-4Ta5d6ff"
@click.Te6edf3self=Ta5d6ff"Ta5d6ffcreateOpen = falseTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffw-full max-w-md rounded-2xl bg-white dark:bg-zinc-900 shadow-2xl border border-gray-200 dark:border-zinc-700Ta5d6ff"
Tff7b72>
Tff7b72<header Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-3 border-b border-gray-200 dark:border-zinc-700 font-semiboldTa5d6ff"Tff7b72>
{{ Te6edf3$t("sticker_packs.create_title") }}
Tff7b72</header>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-4 flex flex-col gap-3Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewPackTitleTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-300 dark:border-zinc-600 px-2 py-1.5 bg-white dark:bg-zinc-800Ta5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffsticker_packs.field_titleTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3maxlength=Ta5d6ff"Ta5d6ff80Ta5d6ff"
Tff7b72/>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewPackShortNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-300 dark:border-zinc-600 px-2 py-1.5 bg-white dark:bg-zinc-800Ta5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffsticker_packs.field_short_nameTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3maxlength=Ta5d6ff"Ta5d6ff32Ta5d6ff"
Tff7b72/>
Tff7b72<textarea
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewPackDescriptionTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-300 dark:border-zinc-600 px-2 py-1.5 bg-white dark:bg-zinc-800Ta5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffsticker_packs.field_descriptionTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3rows=Ta5d6ff"Ta5d6ff2Ta5d6ff"
Te6edf3maxlength=Ta5d6ff"Ta5d6ff280Ta5d6ff"
Tff7b72/>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewPackTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-300 dark:border-zinc-600 px-2 py-1.5 bg-white dark:bg-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffstaticTa5d6ff"Tff7b72>{{ Te6edf3$t("sticker_packs.type_static") }}Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffanimatedTa5d6ff"Tff7b72>{{ Te6edf3$t("sticker_packs.type_animated") }}Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffvideoTa5d6ff"Tff7b72>{{ Te6edf3$t("sticker_packs.type_video") }}Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffmixedTa5d6ff"Tff7b72>{{ Te6edf3$t("sticker_packs.type_mixed") }}Tff7b72</option>
Tff7b72</select>
Tff7b72<label Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 text-smTa5d6ff"Tff7b72>
Tff7b72<input Te6edf3v-model=Ta5d6ff"Ta5d6ffnewPackStrictTa5d6ff" Te6edf3type=Ta5d6ff"Ta5d6ffcheckboxTa5d6ff" Tff7b72/>
{{ Te6edf3$t("sticker_packs.strict_label") }}
Tff7b72</label>
Tff7b72</div>
Tff7b72<footer
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-end gap-2 px-4 py-3 border-t border-gray-200 dark:border-zinc-700 bg-gray-50 dark:bg-zinc-900/50Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-300 dark:border-zinc-600 px-3 py-1.5 text-smTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcreateOpen = falseTa5d6ff"
Tff7b72>
{{ Te6edf3$t("common.cancel") }}
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg bg-blue-600 hover:bg-blue-700 text-white px-3 py-1.5 text-smTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ff!newPackTitleTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffconfirmCreatePackTa5d6ff"
Tff7b72>
{{ Te6edf3$t("sticker_packs.create") }}
Tff7b72</button>
Tff7b72</footer>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import StickerView from "./StickerView.vue";
import ToastUtils from "../../js/ToastUtils.js";
import DialogUtils from "../../js/DialogUtils.js";
export default {
name: "StickerPacksManager",
components: { MaterialDesignIcon, StickerView },
data() {
return {
packs: [],
createOpen: false,
newPackTitle: "",
newPackShortName: "",
newPackDescription: "",
newPackType: "mixed",
newPackStrict: true,
};
},
mounted() {
this.loadPacks();
},
methods: {
async loadPacks() {
try {
const r = await window.api.get("/api/v1/sticker-packs");
this.packs = r.data?.packs || [];
} catch (e) {
console.error(e);
this.packs = [];
}
},
stickerImageUrl(id) {
return `/api/v1/stickers/${id}/image`;
},
openCreatePack() {
this.newPackTitle = "";
this.newPackShortName = "";
this.newPackDescription = "";
this.newPackType = "mixed";
this.newPackStrict = true;
this.createOpen = true;
},
async confirmCreatePack() {
try {
await window.api.post("/api/v1/sticker-packs", {
title: this.newPackTitle,
short_name: this.newPackShortName || null,
description: this.newPackDescription || null,
pack_type: this.newPackType,
is_strict: this.newPackStrict,
});
this.createOpen = false;
ToastUtils.success(this.$t("sticker_packs.created"));
await this.loadPacks();
} catch (e) {
const err = e?.response?.data?.error || "create_failed";
ToastUtils.error(`${this.$t("sticker_packs.create_failed")}: ${err}`);
}
},
async exportPack(pack) {
try {
const r = await window.api.get(`/api/v1/sticker-packs/${pack.id}/export`);
const blob = new Blob([JSON.stringify(r.data, null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
const safe =
(pack.short_name || pack.title || "pack").toLowerCase().replace(/[^a-z0-9_-]+/g, "_") || "pack";
a.href = url;
a.download = `${safe}.meshchatxpack.json`;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
ToastUtils.success(this.$t("sticker_packs.exported"));
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("sticker_packs.export_failed"));
}
},
async deletePack(pack) {
const confirmed = await DialogUtils.confirm(this.$t("sticker_packs.confirm_delete", { title: pack.title }));
if (!confirmed) return;
try {
await window.api.delete(`/api/v1/sticker-packs/${pack.id}?with_stickers=true`);
ToastUtils.success(this.$t("sticker_packs.deleted"));
await this.loadPacks();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("sticker_packs.delete_failed"));
}
},
triggerInstallInput() {
this.$refs.installFileInput?.click();
},
async onInstallFile(event) {
const file = event.target.files?.[0];
event.target.value = "";
if (!file) return;
try {
const text = await file.text();
const doc = JSON.parse(text);
const r = await window.api.post("/api/v1/sticker-packs/install", { ...doc, replace_duplicates: false });
const data = r.data || {};
ToastUtils.success(
this.$t("sticker_packs.installed", {
imported: data.imported || 0,
})
);
await this.loadPacks();
} catch (e) {
const err = e?.response?.data?.error || "install_failed";
ToastUtils.error(`${this.$t("sticker_packs.install_failed")}: ${err}`);
}
},
},
};
Tff7b72</script>
Served by rngit 1.5.2 - Generated in 0.05s